package com.dhecp.core.utils;

import com.dhecp.common.config.Global;
import com.dhecp.common.core.domain.AjaxResult;
import com.dhecp.common.exception.BusinessException;
import com.dhecp.common.utils.TypeUtils;
import com.dhecp.framework.util.ShiroUtils;
import com.dhecp.project.system.domain.SysDictionaryForExport;
import com.dhecp.project.system.domain.SysSetting;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.util.*;

/**
 * 导出Excel
 * @param <T>
 * @author fxz
 * @version 2024-03-05
 */
public class ExportExcelListUtils<T>{

    private static final Logger log = LoggerFactory.getLogger(ExportExcelListUtils.class);

    /**
     * Excel sheet最大行数，默认1000000
     */
    public static final int sheetSize = 1000000;

    /**
     * 工作表名称
     */
    private List<String> sheetName;

    /**
     * 工作薄对象
     */
//    private Workbook wb;
//    private HSSFWorkbook wb;	//office 2003 及以下版本【.xls】
//    private XSSFWorkbook wb;	//office 2007 及以上版本【.xlsx】
    private SXSSFWorkbook wb;	//office 2007 及以上版本【.xlsx】

    /**
     * 工作表对象
     */
    private Sheet sheet;

    /**
     * 样式列表
     */
    private Map<String, CellStyle> styles;

  //导出类型（export:导出数据；template：导入模板）
    private String type;

    /**
     * 导出的数据列表
     */
    private Map<String,List<Object>> list;

    /**
     * 导出的字段列表
     */
    private Map<String,List<Object>> fieldList;

    public T entity;

    /**
     * 对应实体对象
     */
    public Class<T> clazz;

    /**
     * 构造一个导出对象
     * @param clazz 对应实体对象
     */
    @SuppressWarnings("unchecked")
	public ExportExcelListUtils(String moduleTable, T entity){
    	this.entity = entity;
    	this.clazz = (Class<T>) entity.getClass();
    }

    /**
     * 将list中的数据导入到excel表单
     * @param list 导出的数据列表
     * @param fieldList 导出的字段列表
     * @param sheetName 工作表的名称
     * @param type 导出类型（export:导出数据）
     * @return AjaxResult
     */
    public AjaxResult exportExcel(Map<String,List<Object>> list, Map<String,List<Object>> fieldList, List<String> sheetName, String type){
    	if ("export".equals(type)){ //导出类型（export:导出数据；template：导入模板）
            this.list = list;
            this.fieldList = fieldList;
            this.sheetName = sheetName;
        	this.type = type;
    	}
    	//创建一个工作簿
        this.wb = new SXSSFWorkbook(500);
        return exportExcel();
    }

    /**
     * 将创建好的excel导出生成文件
     * @return AjaxResult
     */
    public AjaxResult exportExcel(){
        OutputStream out = null;
        try{
//            // 取出一共有多少个sheet.
            for (int index = 0; index < sheetName.size(); index++){
                createSheet(index);

                // 产生一行
                Row row = sheet.createRow(0);
                int column = 0;
                // 写入各个字段的列头名称
                List<Object> objects = fieldList.get(index);
                for (Object object : objects){
                    SysDictionaryForExport exportField = new ObjectMapper().convertValue(object, SysDictionaryForExport.class);
                    this.createCell(exportField, row, column++);
                }

                if ("export".equals(type)){
                    fillExcelData(index, row);
                }
            }
            String filename = encodingFilename(sheetName.get(0));
            out = new FileOutputStream(getAbsoluteFile(filename));
            wb.write(out);
            return AjaxResult.success(filename);
        }
        catch (Exception e){
            log.error("导出Excel异常{}", e.getMessage());
            throw new BusinessException("导出Excel失败，请联系网站管理员！");
        }
        finally{
            if (wb != null){
                try{
                    wb.close();
                }
                catch (IOException e1){
                    e1.printStackTrace();
                }
            }
            if (out != null){
                try{
                    out.close();
                }
                catch (IOException e1){
                    e1.printStackTrace();
                }
            }
        }
    }

    /**
     * 创建工作表 
     * @param sheetNo sheet数量
     * @param index 序号
     */
    public void createSheet(int index){
        this.sheet = wb.createSheet();
        this.styles = createStyles(wb);
        // 设置工作表的名称
        wb.setSheetName(index, sheetName.get(index));

    }

    /**
     * 创建单元格
     */
    public Cell createCell(SysDictionaryForExport exportField, Row row, int column){
        // 创建列
        Cell cell = row.createCell(column);
        //设置列宽度，第一个参数代表列id(从0开始),第2个参数代表宽度值
    	sheet.setColumnWidth(column, 3766); 
        // 写入列信息
        cell.setCellValue(exportField.getFieldName());
//        setDataValidation(attr, row, column);
        cell.setCellStyle(styles.get("header"));
        return cell;
    }

    /**
     * 填充excel数据
     * @param index 序号
     * @param row 单元格行
     */
    public void fillExcelData(int index, Row row){
    	/*
    	 * 单元格样式的最大数目64000，若在循环内每个单元格创建样式（createDataFormat），数据量大时将超出。
    	 * 因此在循环外预定相关样式，以便选择使用，而不是每次都创建新样式。
    	 */
    	Map<String, CellStyle> cellStyles = new HashMap<String, CellStyle>();
    	CellStyle cellStyle = wb.createCellStyle();		//预设样式
    	CellStyle cellStyle0 = wb.createCellStyle();	//整数样式
    	CellStyle cellStyle1 = wb.createCellStyle();	//1位小数样式
    	CellStyle cellStyle2 = wb.createCellStyle();	//2位小数样式
    	CellStyle cellStyle3 = wb.createCellStyle();	//3位小数样式
    	CellStyle cellStyle4 = wb.createCellStyle();	//4位小数样式
    	CellStyle cellStyle5 = wb.createCellStyle();	//5位小数样式
    	CellStyle cellStyle6 = wb.createCellStyle();	//6位小数样式
    	cellStyle.cloneStyleFrom(styles.get("data"));	//预设样式
    	cellStyle0.cloneStyleFrom(styles.get("data"));	//预设样式基础上添加-整数样式
    	cellStyle1.cloneStyleFrom(styles.get("data"));	//预设样式基础上添加-1位小数样式
    	cellStyle2.cloneStyleFrom(styles.get("data"));	//预设样式基础上添加-2位小数样式
    	cellStyle3.cloneStyleFrom(styles.get("data"));	//预设样式基础上添加-3位小数样式
    	cellStyle4.cloneStyleFrom(styles.get("data"));	//预设样式基础上添加-4位小数样式
    	cellStyle5.cloneStyleFrom(styles.get("data"));	//预设样式基础上添加-5位小数样式
    	cellStyle6.cloneStyleFrom(styles.get("data"));	//预设样式基础上添加-6位小数样式
    	DataFormat df = wb.createDataFormat();
    	cellStyle0.setDataFormat(df.getFormat("0"));
    	cellStyle0.setAlignment(HorizontalAlignment.RIGHT);	//设置单元格的水平对齐类型（居右）
    	cellStyle1.setDataFormat(df.getFormat("0.0"));
    	cellStyle1.setAlignment(HorizontalAlignment.RIGHT);	//设置单元格的水平对齐类型（居右）
    	cellStyle2.setDataFormat(df.getFormat("0.00"));
//    	cellStyle2.setDataFormat(df.getFormat("#,#0.00"));	//含千位分隔符，导入时数值格式分出错
    	cellStyle2.setAlignment(HorizontalAlignment.RIGHT);	//设置单元格的水平对齐类型（居右）
    	cellStyle3.setDataFormat(df.getFormat("0.000"));
    	cellStyle3.setAlignment(HorizontalAlignment.RIGHT);	//设置单元格的水平对齐类型（居右）
    	cellStyle4.setDataFormat(df.getFormat("0.0000"));
    	cellStyle4.setAlignment(HorizontalAlignment.RIGHT);	//设置单元格的水平对齐类型（居右）
    	cellStyle5.setDataFormat(df.getFormat("0.00000"));
    	cellStyle5.setAlignment(HorizontalAlignment.RIGHT);	//设置单元格的水平对齐类型（居右）
    	cellStyle6.setDataFormat(df.getFormat("0.000000"));
    	cellStyle6.setAlignment(HorizontalAlignment.RIGHT);	//设置单元格的水平对齐类型（居右）
    	cellStyles.put("cellStyle", cellStyle);
    	cellStyles.put("cellStyle0", cellStyle0);
    	cellStyles.put("cellStyle1", cellStyle1);
    	cellStyles.put("cellStyle2", cellStyle2);
    	cellStyles.put("cellStyle3", cellStyle3);
    	cellStyles.put("cellStyle4", cellStyle4);
    	cellStyles.put("cellStyle5", cellStyle5);
    	cellStyles.put("cellStyle6", cellStyle6);

        List<Object> objects = list.get(index);
        for (int i = 0; i < objects.size(); i++){
            row = sheet.createRow(i + 1);
            // 得到导出对象
            T rowData = (T) objects.get(i);
            Map<String, Object> dataMap = TypeUtils.entityToMap(rowData);
            int column = 0;
            List<Object> fieldSys = fieldList.get(index);
            for (Object object : fieldSys){
                SysDictionaryForExport exportField = new ObjectMapper().convertValue(object, SysDictionaryForExport.class);
            	String cellValueStr = "";
            	Double cellValueDou = 0.0;
            	Object cellValue = dataMap.get(exportField.getFields());
            	String exportType = "String";
                if (cellValue != null){
                	if (exportField.getFieldFrontTemplet().toString().startsWith("p_")){
                		cellValueStr = cellValue.toString() + "%";
                	}else if ("t_decimal".equals(exportField.getFieldFrontType().toLowerCase()) || "fxsfpsl".equals(exportField.getFields().toLowerCase()) || "fcgfpsl".equals(exportField.getFields().toLowerCase())){
                    	try{
                    		cellValueDou = Double.parseDouble(cellValue.toString());
                    		exportType = "Double";
                    	}catch (Exception e) {
                        	cellValueStr = cellValue.toString();
                    	}
                	}else{
                		cellValueStr = cellValue.toString();
                		//去除数据中自带的HTML样式标签
                		cellValueStr = cellValueStr.replace("<span style=color:red;>", "");
                		cellValueStr = cellValueStr.replace("</span><span style=color:blue;>", "");
                		cellValueStr = cellValueStr.replace("</span>", "");
                	}
                }

                Cell cell = row.createCell(column);
//                cell.setCellStyle(styles.get("data"));	//设置单元格预设样式
                cell.setCellStyle(createCellStyle(cellStyles, exportField, exportType));  //设置单元格样式
                if ("Double".equals(exportType)){
//                	cell.setCellStyle(cellStyle0);
                	cell.setCellValue(cellValueDou);
                }else{
                	cell.setCellValue(cellValueStr);
                }

                column++;
//                this.addCell(excel, row, vo, field, column++);
            }
        }
    }

    /**
     * 设置单元格样式
     * @param wb
     * @return
     * @author DH-Xxj
     */
    private CellStyle createCellStyle(Map<String, CellStyle> cellStyles, SysDictionaryForExport exportField, String exportType){
    	SysSetting sysSetting = ShiroUtils.getSystemSet();	//系统设置信息
    	if ("Double".equals(exportType)){
    		Integer decimals = 2;
    		Integer decimalForPrice = sysSetting.getDecimalForPrice();		//价格小数位数
            Integer decimalForSum = sysSetting.getDecimalForSum();			//金额小数位数
            Integer decimalForNumber = sysSetting.getDecimalForNumber();	//数量小数位数
            Integer decimalForPercent = sysSetting.getDecimalForPercent();	//百分比小数位数
    		if ("t_price".equals(exportField.getFieldFrontTemplet().toString())){
    			decimals = decimalForPrice;
    		}else if ("t_sum".equals(exportField.getFieldFrontTemplet().toString())){
    			decimals = decimalForSum;
    		}else if ("t_number".equals(exportField.getFieldFrontTemplet().toString())){
    			decimals = decimalForNumber;
    		}else if ("t_percent".equals(exportField.getFieldFrontTemplet().toString()) || "p_percent".equals(exportField.getFieldFrontTemplet().toString())){
    			decimals = decimalForPercent;
    		}else if ("t_appoint1".equals(exportField.getFieldFrontTemplet().toString()) || "p_appoint1".equals(exportField.getFieldFrontTemplet().toString())){
    			decimals = 1;
    		}else if ("t_appoint2".equals(exportField.getFieldFrontTemplet().toString()) || "p_appoint2".equals(exportField.getFieldFrontTemplet().toString())){
    			decimals = 2;
    		}else if ("t_appoint3".equals(exportField.getFieldFrontTemplet().toString()) || "p_appoint3".equals(exportField.getFieldFrontTemplet().toString())){
    			decimals = 3;
    		}else if ("t_appoint4".equals(exportField.getFieldFrontTemplet().toString()) || "p_appoint4".equals(exportField.getFieldFrontTemplet().toString())){
    			decimals = 4;
    		}else if ("t_appoint5".equals(exportField.getFieldFrontTemplet().toString()) || "p_appoint5".equals(exportField.getFieldFrontTemplet().toString())){
    			decimals = 5;
    		}else if ("t_appoint6".equals(exportField.getFieldFrontTemplet().toString()) || "p_appoint6".equals(exportField.getFieldFrontTemplet().toString())){
    			decimals = 6;
    		}else{
    			decimals = 2;
    		}

    		//根据字典配置设置的小数点位数选择对应样式
        	if (decimals == 0) return cellStyles.get("cellStyle0");
        	else if (decimals == 1) return cellStyles.get("cellStyle1");
        	else if (decimals == 2) return cellStyles.get("cellStyle2");
        	else if (decimals == 3) return cellStyles.get("cellStyle3");
        	else if (decimals == 4) return cellStyles.get("cellStyle4");
        	else if (decimals == 5) return cellStyles.get("cellStyle5");
        	else if (decimals == 6) return cellStyles.get("cellStyle6");
        	else return cellStyles.get("cellStyle2");
    	}else{
    		return cellStyles.get("cellStyle");
    	}
    }
    
    /**
     * 创建表格样式
     * @param wb 工作薄对象
     * @return 样式列表
     */
    private Map<String, CellStyle> createStyles(SXSSFWorkbook wb){
        // 写入各条记录,每条记录对应excel表中的一行
        Map<String, CellStyle> styles = new HashMap<String, CellStyle>();
        CellStyle style = wb.createCellStyle();
        style.setAlignment(HorizontalAlignment.LEFT);	//设置单元格的水平对齐类型（居左）
        style.setVerticalAlignment(VerticalAlignment.CENTER);	//设置单元格的垂直对齐类型（居中）
        style.setBorderRight(BorderStyle.THIN);	//设置单元格右边框的边框类型
        style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());	//设置右边边框的颜色
        style.setBorderLeft(BorderStyle.THIN);	//设置单元格左边框的边框类型
        style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());	//设置左边框的颜色
        style.setBorderTop(BorderStyle.THIN);	//设置单元格上边框使用的边框类型
        style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());	//设置上边框的颜色
        style.setBorderBottom(BorderStyle.THIN);	//设置单元格下边框使用的边框类型
        style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());	//设置下边框的颜色
        Font dataFont = wb.createFont();
//      headerFont.setFontName("Arial");	//设置字体的名称(例如:Arial)
        dataFont.setFontName("仿宋");	//设置字体的名称(例如:仿宋)
        dataFont.setFontHeightInPoints((short) 12);	//设置字体高度
        style.setFont(dataFont);
        styles.put("data", style);

        style = wb.createCellStyle();
        style.cloneStyleFrom(styles.get("data"));
        style.setAlignment(HorizontalAlignment.CENTER);	//设置单元格的水平对齐类型（居中）
        style.setVerticalAlignment(VerticalAlignment.CENTER);	//设置单元格的垂直对齐类型（居中）
        style.setFillForegroundColor(IndexedColors.GREY_50_PERCENT.getIndex());	//
        style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        Font headerFont = wb.createFont();
//        headerFont.setFontName("Arial");	//设置字体的名称(例如:Arial)
        dataFont.setFontName("仿宋");	//设置字体的名称(例如:仿宋)
        headerFont.setFontHeightInPoints((short) 12);	//设置字体高度
        headerFont.setBold(true);
        headerFont.setColor(IndexedColors.WHITE.getIndex());
        style.setFont(headerFont);
        styles.put("header", style);

        return styles;
    }

    /**
     * 编码文件名
     */
    public String encodingFilename(String filename)
    {
        filename = UUID.randomUUID().toString() + "_" + filename + ".xlsx";
        return filename;
    }

    /**
     * 获取下载路径
     * 
     * @param filename 文件名称
     */
    public String getAbsoluteFile(String filename)
    {
        String downloadPath = Global.getDownloadPath() + filename;
        File desc = new File(downloadPath);
        if (!desc.getParentFile().exists())
        {
            desc.getParentFile().mkdirs();
        }
        return downloadPath;
    }

}
